use std::borrow::Cow;
use axum::{
    async_trait, 
    extract::{rejection::*, FromRequest, RequestParts},
    http::{StatusCode, HeaderValue}, 
    BoxError, 
    response::{IntoResponse, Response},
    body::{self, Full}
};
use hyper::header;
use serde::{de::DeserializeOwned, Serialize};
use serde_json::{Value, json};

pub struct Json<T>(pub T);

#[async_trait]
impl<B, T> FromRequest<B> for Json<T>
where
    T: DeserializeOwned,
    B: axum::body::HttpBody + Send,
    B::Data: Send,
    B::Error: Into<BoxError>,
{
    type Rejection = (StatusCode, axum::Json<Value>);

    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
        match axum::Json::<T>::from_request(req).await {
            Ok(value) => Ok(Self(value.0)),
            Err(rejection) => {
                let (status, body): (_, Cow<'_, str>) = match rejection {
                    JsonRejection::JsonDataError(_err) => (
                        StatusCode::BAD_REQUEST,
                        format!("传递参数缺失").into(),
                    ),
                    JsonRejection::MissingJsonContentType(_err) => {
                        (
                            StatusCode::BAD_REQUEST,
                            format!("Content-Type类型必须为application/json").into(),
                        )
                    }
                    _err => (
                        StatusCode::INTERNAL_SERVER_ERROR,
                        format!("内部错误500,请联络管理员").into(),
                    ),
                };

                Err((
                    status,
                    axum::Json(json!({
                        "msg": body,
                        "success":false,
                    })),
                ))
            }
        }
    }
}

impl<T> IntoResponse for Json<T>
where
    T: Serialize,
{
    fn into_response(self) -> Response {
        let bytes = match serde_json::to_vec(&self.0) {
            Ok(res) => res,
            Err(err) => {
                return Response::builder()
                    .status(StatusCode::INTERNAL_SERVER_ERROR)
                    .header(
                        header::CONTENT_TYPE,
                        HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
                    )
                    .body(body::boxed(Full::from(err.to_string())))
                    .unwrap();
            }
        };

        let mut res = Response::new(body::boxed(Full::from(bytes)));
        res.headers_mut().insert(
            header::CONTENT_TYPE,
            HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
        );
        res
    }
}